package com.instpower.things.instpower;

import com.instpower.things.helper.Checksum;
import com.instpower.things.service.InstService;
import com.instpower.things.transport.*;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.concurrent.ScheduledFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName InstpowerHandler
 * @Author: hellmash@gmail.com
 * @Date: 2020/2/24
 * @Version: 1.0.0
 * @Description: 处理器
 */
public class InstpowerHandler extends SimpleChannelInboundHandler<Request> {

    private static final Logger logger = LoggerFactory.getLogger("SYS");

    private final ConnectionManager connectionManager = ConnectionManager.getInstance();

    private final InstService instService = Context.getBean("instService");

    /**
     * netty定时任务,间隔时间 30*60L;
     */
    public static Long time = 1500L;

    private ScheduledFuture<?> LoopQueryCabinetThread;


    /**
     * 关闭通道
     */
    static void closeOnFlush(Channel ch) {
        if (ch.isActive()) {
            ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        connectionManager.create(ctx);
        logger.info("channelActive 新的连接{},当前连接数: {}", ctx.channel().id().asShortText(), connectionManager.count());

        /**
         * 创建netty定时任务
         */
        LoopQueryCabinetThread = ctx.channel().eventLoop().scheduleAtFixedRate(() -> {
            //todo 如果查不出sn,需要一个机制
            String sn = getSn(ctx);
            //组建08F6指令
            Response data08F6 = new Response();
            Header header08F6 = new Header();
            header08F6.setPtrol((byte) 0x02);
            header08F6.setAddr(sn);
            header08F6.setCommand((short) 0x08F6);
            header08F6.setLength((short) (2));
            data08F6.setHeader(header08F6);
            DataBuffer buffer08F6 = data08F6.encode();
            int checksum08F6 = Checksum.crc16(Checksum.CRC16_ANKER,
                    ByteBuffer.wrap(buffer08F6.readByteArray(buffer08F6.readableBytes())));
            //do release after read
            buffer08F6.release();
            Ender ender08F6 = new Ender();
            ender08F6.setChecksum(checksum08F6);
            data08F6.setEnder(ender08F6);
            logger.info("服务器开始定时下发查询,下发机柜:{},下发时间:{},下发指令:{},下发数据:{}", sn, System.nanoTime(), data08F6.getHeader().getCommand(), data08F6);
            ctx.writeAndFlush(data08F6);
        }, 300 + new Random().nextInt(Math.toIntExact(time)), time, TimeUnit.SECONDS);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        String sn = getSn(ctx);
        connectionManager.remove(ctx);
        logger.debug("断开的连接{},断开的连接sn:{}, 当前连接数: {}", ctx.channel().id().asShortText(), sn, connectionManager.count());
        /**
         * 关闭这个ctx的定时任务
         */
        LoopQueryCabinetThread.cancel(false);
    }

    private String getSn(ChannelHandlerContext ctx) {
        logger.info("getSn,map中数据是:{}", DeviceSessionManager.getInstance().sessions());
        String id = ctx.channel().id().asShortText();
        String sn = null;
        Map<String, DeviceSession> sessions = DeviceSessionManager.getInstance().sessions();
        for (String snKey : sessions.keySet()) {
            if (sessions.get(snKey).getMConnection().getId().equals(id)) {
                sn = snKey;
            }
        }
        return sn;
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, final Request request) throws Exception {
        Connection conn = connectionManager.find(ctx);
        if (conn == null) {
            ctx.fireChannelInactive();
            return;
        }
        if (request == null) {
            connectionManager.remove(ctx);
            ctx.fireChannelInactive();
            logger.debug("收到的Request为NULL,断开连接!");
            return;
        }
        /*
        String cabinetSN = request.getHeader().getAddr();
        DeviceSession session = DeviceSessionManager.getInstance().get(cabinetSN);
        //
        if(session != null) {
            if (conn.getId().equals(session.getMConnection().getId())) {
                logger.debug("当前sessionId:{}, ConnectionId:{}", cabinetSN, session.getMConnection().getId());
            } else {
                DeviceSessionManager.getInstance().add(new DeviceSession(conn, cabinetSN));
            }
        }else{
            //登录完成，设置会话
            DeviceSessionManager.getInstance().add(new DeviceSession(conn, cabinetSN));
        }*/
        String cabinetSN = request.getHeader().getAddr();
        String cmd = String.format("Command = %04x", request.getHeader().getCommand());

        DeviceSession session = DeviceSessionManager.getInstance().get(cabinetSN);
        logger.debug("会话数总数：{}, 当前会话ID：{} , 会话连接ID：{}, 当前Conn ID: {}",
                DeviceSessionManager.getInstance().sessions().size(),
                request.getHeader().getAddr(),
                session == null ? "无连接" : session.getMConnection().getId(),
                ctx.channel().id().asShortText());
        /**
         * 任务派遣
         */
        logger.debug("sn:{} dispatch {}", cabinetSN, cmd);
        this.dispatch(ctx, request);
        logger.debug("sn:{} dispatch end {}", cabinetSN, cmd);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        String id = ctx.channel().id().asShortText();
        String sn = getSn(ctx);
        if (cause == null) {
            logger.error("InstPower Channel {}  异常!,对应机柜sn:{}", ctx.channel().id() + "@" + ctx.channel().remoteAddress(), sn);
        } else {
            logger.error("Channel {},对应机柜sn:{} 异常: {} !", id + "@" + ctx.channel().remoteAddress(), sn, cause.getLocalizedMessage());
            logger.error(cause.getLocalizedMessage(), cause);
        }
        InstpowerHandler.closeOnFlush(ctx.channel());
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            logger.debug("获得触发器:{}", ((IdleStateEvent) evt).state());
            IdleState e = ((IdleStateEvent) evt).state();
            if (e == IdleState.ALL_IDLE) {
                String sn = getSn(ctx);
                logger.debug("Cabinet:{} offline,No Data from socket connection for 1 min.", sn);
                ConnectionManager.getInstance().find(ctx).close();
                //服务端强制断开连接
                ctx.fireChannelInactive();
                ctx.close();
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }

    /**
     * 任务控制器
     *
     * @param ctx
     * @param request
     */
    private void dispatch(ChannelHandlerContext ctx, Request request) throws Exception {
        Connection conn = connectionManager.find(ctx);
        synchronized (conn) {
            if (conn == null || request == null) {
                return;
            }

            Header header = request.getHeader();
            short command = header.getCommand();
            //判断是否有效数据,非法数据就关闭该通道,让其重新再连接
            if (!header.checkFlag()) {
                logger.info("conn close! flag = " + header.getSyn());
                conn.close();
                return;
            }

            Handler handler = HandlerManager.getInstance().find(command);
            if (handler != null) {
                logger.info("start execute");
                handler.setServerHandler(instService);

                handler.execute(conn, request);

            } else {
                logger.info(" do not support this protocol: " + command);
            }
        }

    }

}
